package org.jproxy.json;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jproxy.exception.CannotCastClassException;
import org.jproxy.exception.CannotCreateNewInstanceException;


class ComplexJSONObject implements JSONObject
{
	private final Map<String, JSONAttribute> attributeMap;
	private final Iterator<JSONAttribute> it;

	ComplexJSONObject(List<JSONAttribute> attributeList)
	{
		attributeMap = initJSONAttributeMap(attributeList);
		it = Collections.unmodifiableList(attributeList).iterator();
	}

	private Map<String, JSONAttribute> initJSONAttributeMap(List<JSONAttribute> attributeList)
	{
		Map<String, JSONAttribute> map = new HashMap<String, JSONAttribute>();
		for(JSONAttribute attribute : attributeList)
			map.put(attribute.getName(), attribute);
		return Collections.unmodifiableMap(map);
	}

	@Override
	public JSONAttribute getAttribute()
	{
		return it.next();
	}

	@Override
	public JSONAttribute getAttribute(String name)
	{
		return attributeMap.get(name);
	}

	@Override
	public Type getType()
	{
		return Type.OBJECT;
	}

	@Override
	public <T> T toObject(Class<T> clazz)
	{
		T result = newInstance(clazz);
		if(result == null)
			return null;
		int matches = 0;
		for(Method method : clazz.getDeclaredMethods())
		{
			String name = method.getName();
			if(name.startsWith("set"))
			{
				name = new StringBuilder().append(Character.toLowerCase(name.charAt(3))).append(name.substring(4)).toString();
				JSONAttribute attribute = getAttribute(name);
				if(attribute != null)
				{
					setMethodValue(method, result, attribute.getJSONObject().toObject(method.getParameterTypes()[0]));
					matches++;
				}
			}
		}
		if(matches != attributeMap.size())
			throw new CannotCastClassException(clazz);
		return result;
	}

	private <T> T newInstance(Class<T> clazz)
	{
		try
		{
			return clazz.newInstance();
		}catch(Throwable t)
		{
			throw new CannotCreateNewInstanceException(clazz);
		}
	}

	private void setMethodValue(Method method, Object obj, Object... args)
	{
		try
		{
			method.invoke(obj, args);
		} catch (IllegalArgumentException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
